/**
 * Kernel.c
 * Description:
 * Hamish Cumming, Brian Thorne 2008
 */

#include "kernel.h"
#include <avr\interrupt.h>

/* Private Functions */
static uint8_t (*tasks[MAX_NUM_TASKS]) (void);
static uint8_t task_state[MAX_NUM_TASKS];
static void clock_init(void);

/* Public Variables */
volatile uint8_t task_flags[MAX_NUM_TASKS]; //global, for ISR's to modify

/* Private Variables */
static time_t clock;

ISR (TIMER1_COMPA_vect)
{
    /* ISR for Timer 1 Compare Match A */
    //doesn't check for overflow so don't run too long!! (like REALLY long)
    clock.time8[2]++;
    if (clock.time8[2] == 0)
    {
        clock.time8[3]++;
    }
}

/** Registers a task for round robin scheduling */
uint8_t register_task (uint8_t (*task) (void), uint8_t task_index)
{
    if (task_index >= MAX_NUM_TASKS)
        return EXIT_FAIL;
    tasks[task_index] = task;
    return EXIT_SUCCESS;
}

/** Activates a task */
uint8_t activate_task (uint8_t task_index)
{
    task_state[task_index] = ACTIVATED;
    return true;
}

/** Deactivates a task */
uint8_t deactivate_task(uint8_t task_index)
{
    task_state[task_index] = DEACTIVATED;
    return true;
}

/**
 * Initialises the system clock
 */
static void clock_init(void)
{
    TCCR1B  = _BV (WGM12);  /* Set CTC mode, OCR1A, WGM1 = 0100  */
    TCCR1B |= _BV (CS10);   /* Set prescaler to /1, CS1 = 010    */
    OCR1A   = 0xFFFF;       /* Output compare register to 65535, could just use overflow interrupt here, but wateva    */
    TIMSK1  = _BV (OCIE1A); /* Enable OC1A int. on OCF1 flag.    */
}

/** Initialise the kernel */
uint8_t kernel_init()
{
    clock_init();
    //Deactivate all tasks
    for (int i = 0; i < MAX_NUM_TASKS;i++)
    {
        task_state[i] = DEACTIVATED;
    }
    sei ();     /* Enable global interrupts */
    return EXIT_SUCCESS;
}

/** Run the kernel, schedule background tasks */
uint8_t kernel_run(void)
{

    while (true)
    {
        /* Poll all background tasks */
        for (int i=0; i<MAX_NUM_TASKS; i++)
        {
            if (task_flags[i] == ACTIVATED)
            {
                //ui_message("tf act"); //Debugging only
                activate_task(i);
                task_flags[i] = DEACTIVATED;
            }

            if (task_state[i] == ACTIVATED)
            {
                //ui_message("ts act");
                if (tasks[i]() == EXIT_SLEEP)
                {
                    deactivate_task(i);
                }
            }
        }

    }
    return EXIT_SUCCESS;
}

/** Returns the current clock value (with microsecond precision) */
time_t get_clock_value(void)
{
    //assumes we are on a little endian machine
    clock.time8[0] = TCNT1L;
    clock.time8[1] = TCNT1H;
    return clock;
}
